package com.ophiux.authservice.config;

import com.ophiux.authservice.handler.FormAuthenticationFailureHandler;
import com.ophiux.authservice.handler.SsoLogoutSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;

/**
 * @desc: 开启Spring Security配置
 * @author: hhl
 * @date：2022/10/25 9:30
 */
@Configuration
@Order(90)
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService hncdmUserDetailsService;

    /**
     * 配置安全拦截策略
     * @param http the {@link HttpSecurity} to modify
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        http.formLogin().loginPage("/token/login").loginProcessingUrl("/token/form")
//                .successHandler(tenantSavedRequestAwareAuthenticationSuccessHandler())
//                .failureHandler(authenticationFailureHandler()).and().logout()
//                .logoutSuccessHandler(logoutSuccessHandler()).deleteCookies("JSESSIONID").invalidateHttpSession(true)
//                .and().authorizeRequests().antMatchers("/token/**", "/actuator/**", "/mobile/**").permitAll()
//                .anyRequest().authenticated().and().csrf().disable().apply(mobileSecurityConfigurer());
        //todo 允许表单登录
        http.formLogin()
            //登录访问controller路径
            .loginPage("/token/login")
            //表单提交地址
            .loginProcessingUrl("/login/form")
            //登录成功后跳转界面
            .defaultSuccessUrl("/token/login?error=login success")
//          .successHandler(formAuthenticationSuccessHandler())
            //表单提交失败处理
            .failureHandler(formAuthenticationFailureHandler())
            //退出登录处理
            .and().logout().logoutSuccessHandler(ssoLogoutSuccessHandler()).deleteCookies("JSESSIONID").invalidateHttpSession(true)
            //不需要认证的接口
            .and().authorizeRequests().antMatchers("/token/**","/actuator/**").permitAll()
            //剩下的所有请求，只要认证后的都可以访问
            .anyRequest().authenticated()
            .and()
            .csrf()
            .disable();
    }

    @Bean
    public FormAuthenticationFailureHandler formAuthenticationFailureHandler(){
        return new FormAuthenticationFailureHandler();
    }
//    @Bean
//    public FormAuthenticationSuccessHandler formAuthenticationSuccessHandler(){
//        return new FormAuthenticationSuccessHandler();
//    }
    @Bean
    public SsoLogoutSuccessHandler ssoLogoutSuccessHandler(){
        return new SsoLogoutSuccessHandler();
    }


    /**
     * 配置用户
     * @param auth the {@link AuthenticationManagerBuilder} to use
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //TODO 暂定从内存中加载用户，实际生产中需要从数据库中加载
//        auth.inMemoryAuthentication()
//                .withUser("admin")
//                .password(new BCryptPasswordEncoder().encode("123456"))
//                .roles("admin")
//                .and()
//                .withUser("user")
//                .password(new BCryptPasswordEncoder().encode("123456"))
//                .roles("user");
        auth.userDetailsService(hncdmUserDetailsService);
        auth.authenticationProvider(preAuthenticatedAuthenticationProvider());
    }

    /**
     * 注入认证管理器AuthenticationManager
     * AuthenticationManager对象在OAuth2认证服务中要使用，提前放入IOC容器中
     * Oauth的密码模式需要
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 不拦截静态资源
     * @param web
     */
    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers("/favicon.ico", "/css/**", "/error");
    }

    /**
     * 加密算法
     * https://spring.io/blog/2017/11/01/spring-security-5-0-0-rc1-released#password-storage-updated
     * Encoded password does not look like BCrypt
     * @return PasswordEncoder
     */
    @Bean
    PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Bean
    public PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider(){
        //  log.info("Configuring pre authentication provider");
        UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> wrapper = new UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>(hncdmUserDetailsService);
        PreAuthenticatedAuthenticationProvider it = new PreAuthenticatedAuthenticationProvider();
        it.setPreAuthenticatedUserDetailsService(wrapper);

        return it;
    }
}